package xclient

import (
	"errors"
	"log"
	"math"
	"math/rand"
	"net/http"
	"strings"
	"sync"
	"time"
)

type SelectMode int

const (
	RandomSelect SelectMode = iota
	RoundRobinSelect
)

type Discovery interface {
	Refresh() error
	Update(servers []string) error
	Get(mode SelectMode) (string,error)
	GetAll() ([]string,error)
}

type MultiServerDiscovery struct {
	r *rand.Rand
	mu sync.RWMutex
	servers []string
	index int
}

func NewMultiServerDiscovery(servers []string) *MultiServerDiscovery {
	d := &MultiServerDiscovery{
		servers: servers,
		r: rand.New(rand.NewSource(time.Now().UnixNano())),
	}
	d.index = d.r.Intn(math.MaxInt32 - 1)
	return d
}

var _ Discovery = (*MultiServerDiscovery)(nil)

func (d *MultiServerDiscovery) Refresh() error {
	return nil
}

func (d *MultiServerDiscovery) Update(servers []string) error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.servers = servers
	return nil
}

func (d *MultiServerDiscovery) Get(mode SelectMode) (string,error) {
	d.mu.Lock()
	defer d.mu.Unlock()
	n := len(d.servers)
	if n==0 {
		return "",errors.New("rpc discovery: no available servers")
	}
	switch mode {
	case RandomSelect:
		return d.servers[d.r.Intn(n)],nil
	case RoundRobinSelect:
		s := d.servers[d.index%n]
		d.index = (d.index+1)%n
		return s,nil
	default:
		return "",errors.New("rpc discovery: not supported select mode")
	}
}

func (d *MultiServerDiscovery) GetAll() ([]string,error) {
	d.mu.Lock()
	defer d.mu.Unlock()
	servers := make([]string,len(d.servers),len(d.servers))
	copy(servers,d.servers)
	return servers,nil
}

type RegistryDiscovery struct {
	*MultiServerDiscovery
	//服务中心地址
	registry string
	//服务列表过期时间
	timeout time.Duration
	//最后一次从服务中心更新列表的时间
	lastUpdate time.Time
}

const defaultUpdateTimeout = time.Second * 10

func NewRegistryDiscovery(registerAddr string , timeout time.Duration) *RegistryDiscovery {
	if timeout == 0 {
		timeout = defaultUpdateTimeout
	}
	d := &RegistryDiscovery{
		MultiServerDiscovery: NewMultiServerDiscovery(make([]string,0)),
		registry: registerAddr,
		timeout: timeout,
	}
	return d
}

func (d *RegistryDiscovery) Update(servers []string) error {
	d.mu.Lock()
	defer d.mu.Unlock()
	d.servers = servers
	d.lastUpdate = time.Now()
	return nil
}

func (d *RegistryDiscovery) Refresh() error {
	d.mu.Lock()
	defer d.mu.Unlock()
	if d.lastUpdate.Add(d.timeout).After(time.Now()) {
		return nil
	}
	log.Println("rpc registry servers from registry",d.registry)
	resp, err := http.Get(d.registry)
	if err != nil {
		log.Println("rpc registry refresh err:",err)
		return err
	}
	servers := strings.Split(resp.Header.Get("X-Servers"),",")
	d.servers = make([]string,0,len(servers))
	for _,server := range servers {
		if strings.TrimSpace(server) != "" {
			d.servers = append(d.servers,strings.TrimSpace(server))
		}
	}
	d.lastUpdate = time.Now()
	return nil
}

func (d *RegistryDiscovery) Get(mode SelectMode) (string,error) {
	if err := d.Refresh();err != nil {
		return "",err
	}
	return d.MultiServerDiscovery.Get(mode)
}

func (d *RegistryDiscovery) GetAll() ([]string,error) {
	if err := d.Refresh();err != nil {
		return nil,err
	}
	return d.MultiServerDiscovery.GetAll()
}
